package org.dromara.system.kernel.channel;

import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.encrypt.utils.EncryptUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.system.domain.bo.cone.ConePushBo.Meta;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.system.domain.PayAccount;
import org.dromara.system.domain.PayGroupAccount;
import org.dromara.system.domain.PayOrder;
import org.dromara.system.domain.PayOrderLog;
import org.dromara.system.domain.bo.aparams.AOrderBo;
import org.dromara.system.domain.bo.cone.ConePushBo;
import org.dromara.system.domain.bo.cone.ConeResult;
import org.dromara.system.domain.bo.cone.ConeUpdateBo;
import org.dromara.system.domain.bo.cparams.OrderProduct;
import org.dromara.system.domain.bo.cparams.OrderUpdateBo;
import org.dromara.system.kernel.OrderUpdateKernel;
import org.dromara.system.service.IPayAccountService;
import org.dromara.system.service.IPayOrderLogService;
import org.dromara.system.service.IPayOrderService;
import org.dromara.system.utils.ConversionUtils;
import org.dromara.system.utils.RequestUtils;
import org.dromara.system.utils.UserAgentUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@RequiredArgsConstructor
@Service
public class ConeOrderKernel {

    private final IPayOrderLogService orderLogService;
    private final IPayOrderService orderService;
    private final IPayOrderService payOrderService;
    private final OrderUpdateKernel orderUpdateKernel;
    private final IPayAccountService accountService;
    @Value("${gatewayDomain}")
    public String gatewayDomain;

    /**
     * 获取支付链接
     *
     * @param account      收款账号
     * @param groupAccount 轮询组收款账号配置
     * @param order        订单信息
     */
    public R<PayOrder> requestPayLink(PayAccount account, PayGroupAccount groupAccount, PayOrder order, PayOrderLog log) {

        //获取账号请求地址
        String url = account.getDomain();
        if (StringUtils.isEmpty(url)) {
            return R.fail("DomainUrl is invalid", order);
        }

        //构建参数
        JSONObject params = buildParams(groupAccount, account, order);
        if (params == null) {
            return R.fail("no config params found", order);
        }

        //请求
        long time = System.currentTimeMillis();
        String res;
        try {
            Map<String, String> headers = new HashMap<>();
            headers.put("Referer", params.getString("site"));
            res = new RequestUtils().doPostJsonNoTry(url, params.toJSONString(), headers);
        } catch (Exception e) {
            res = "请求失败：" + e.getMessage();
        }

        //记录请求日志
        orderLogService.saveLog(log != null ? log.getLogId() : null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "获取支付链接", params.toString(JSONWriter.Feature.WriteMapNullValue), res, log != null ? System.currentTimeMillis() - time : System.currentTimeMillis());

        try {
            if (StringUtils.isEmpty(res) || res.startsWith("请求失败")) {
                return R.fail("Request link null", order);
            }

            //解析返回参数
            ConeResult result;
            try {
                result = JSONObject.parseObject(res, ConeResult.class);
            } catch (Exception e) {
                return R.fail(res, order);
            }

            if (!Objects.equals(result.getCode(), 2)) {
                return R.fail(result.getMsg(), order);
            }

            String payUrl = result.getUrl();

            //写到订单对象里，返出去使用
            order.setLink(payUrl);

            //更新订单
            PayOrder up = new PayOrder();
            up.setOrderId(order.getOrderId());
            up.setLink(payUrl);
            orderService.updatePayOrderAsync(up);

            //记录更新日志
            orderLogService.saveLog(log != null ? log.getLogId() : null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "更新B端订单信息", JSONObject.toJSONString(up), "success", 10L);

            return R.ok(order);

        } catch (Exception e) {
            return R.fail("Request link fail", order);
        }
    }

    /**
     * 更新订单
     *
     * @param cBo 参数
     * @return JSONObject
     */
    public JSONObject updateOrder(ConeUpdateBo cBo) {
        JSONObject result = new JSONObject().fluentPut("result", "failed");

        if (StringUtils.isEmpty(cBo.getFporder_id())) {
            return result.fluentPut("msg", "fporder_id is valid");
        }

        Long orderId = null;
        try {
            orderId = Long.valueOf(cBo.getFporder_id().split("_")[0]);
        } catch (Exception ignore) {
        }
        if (orderId == null) {
            return result.fluentPut("msg", "fporder_id is valid !");
        }
        if (!RedisUtils.tryLock("update_order_a_" + orderId, 10, 10)) {
            return result.fluentPut("msg", "order is updating");
        }
        PayOrder order = payOrderService.selectById(orderId);
        if (order == null) {
            return result.fluentPut("msg", "Order does not exist");
        }

        OrderUpdateBo bo = new OrderUpdateBo();
        bo.setOrder_id(orderId);
        bo.setPayment_id(cBo.getTransactionID());

        //处理订单状态
        String status = cBo.getPayment_status();
        switch (status) {
            case "cancelled":
                bo.setCode(3);
                bo.setFail_msg(cBo.getMsg());
                result.fluentPut("result", "success");
                break;
            case "Failed":
                bo.setCode(3);
                bo.setFail_msg(cBo.getMsg());
                result.fluentPut("result", "failed").fluentPut("url", order.getFailureUrl()).fluentPut("msg", "404 status Failed");
                break;
            case "Success":
                bo.setCode(2);
                result.fluentPut("result", "success").fluentPut("url", order.getSuccessUrl()).fluentPut("msg", "status Success");
                break;
            case "Refund":
                bo.setCode(3);
                bo.setFail_msg(cBo.getMsg());
                result.fluentPut("result", "Refunded").fluentPut("msg", "status Refunded");
                try {
                    //收款账号
                    PayAccount account = accountService.selectById(order.getAccountId());
                    JSONObject config = account.getAccountConfig();
                    result.fluentPut("url", config.getString("webUrl"));
                } catch (Exception ignore) {
                }
                break;
            case "Completed":
                bo.setCode(2);
                result.fluentPut("result", "success").fluentPut("url", order.getSuccessUrl()).fluentPut("msg", "status Completed");
                break;
        }

        //新增一条订单日志
        JSONObject upJson = JSONObject.from(bo);
        upJson.put("response", cBo);

        PayOrderLog log = orderLogService.saveLog(null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "平台更新订单", upJson.toJSONString(), null, System.currentTimeMillis());

        R<?> res;
        if (RedisUtils.tryLock("update_order_" + order.getOrderId(), 10, 10)) {
            //更新订单
            res = orderUpdateKernel.updateOrder(order, bo, log);
            RedisUtils.unlock("update_order_" + order.getOrderId());
        } else {
            res = R.fail("还有未执行完成的订单更新任务");
        }

        //更新日志信息
        orderLogService.updateLog(log.getLogId(), order.getOrderId(), JSONObject.toJSONString(res, JSONWriter.Feature.WriteMapNullValue), System.currentTimeMillis() - log.getTime());

        RedisUtils.unlock("update_order_a_" + orderId);

        return R.isError(res) ? result.fluentPut("result", "failed").fluentPut("msg", res.getMsg()) : result;
    }

    /**
     * 构建请求参数
     *
     * @param order 订单
     * @return
     */
    private JSONObject buildParams(PayGroupAccount groupAccount, PayAccount account, PayOrder order) {

        //获取账号配置
        JSONObject config = account.getAccountConfig();

        String spayId = config.getString("spayId"); //商户ID
        String payerName = config.getString("payerName"); //商户名称
        String secretKey = config.getString("secretKey"); //商户密匙
        String webName = config.getString("webName"); //网站名称
        String webUrl = config.getString("webUrl"); //网url

        if (spayId == null || secretKey == null || payerName == null || webName == null || webUrl == null) {
            return null;
        }

        String currency; //币别
        BigDecimal amount; //金额
        if (Objects.equals(groupAccount.getIsForce(), 1) && order.getForceCurrency() != null) {
            currency = order.getForceCurrency();
            amount = order.getForceMoney();
        } else {
            currency = order.getCurrency();
            amount = order.getCMoney();
        }

        amount = amount.setScale(2, RoundingMode.HALF_UP);

        AOrderBo.Address bill = JSONObject.parseObject(order.getCustomer(), AOrderBo.Address.class);
        AOrderBo.Address shipping = JSONObject.parseObject(order.getShipping(), AOrderBo.Address.class);

        //构建参数
        ConePushBo sa = new ConePushBo();
        sa.setSpayid(spayId);
        sa.setSign(EncryptUtils.encryptByMd5(spayId + payerName + webUrl + amount + currency + secretKey));
        sa.setToken("");
        sa.setToken_id("");
        sa.setAmount(amount.toPlainString());
        sa.setCurrency(currency);
        sa.setIp(order.getCustomerIp());
        sa.setEmail(order.getCustomerEmail());
        sa.setPhone(order.getCustomerPhone());
        sa.setIxun_number(null);
        sa.setIxun_expiry("/");
        sa.setIxun_cvc(null);
        sa.setShipping_firstname(shipping.getFirstName());
        sa.setShipping_lastname(shipping.getLastName());
        sa.setShipping_company("");
        sa.setShipping_address_1(shipping.getAddress1());
        sa.setShipping_address_2(shipping.getAddress2());
        sa.setShipping_postcode(shipping.getPostcode());
        sa.setShipping_city(shipping.getCity());
        sa.setShipping_state(shipping.getState());
        sa.setShipping_country(shipping.getCountry());
        sa.setBilling_firstname(bill.getFirstName());
        sa.setBilling_lastname(bill.getLastName());
        sa.setBilling_company("");
        sa.setBilling_address_1(bill.getAddress1());
        sa.setBilling_address_2(bill.getAddress2());
        sa.setBilling_postcode(bill.getPostcode());
        sa.setBilling_city(bill.getCity());
        sa.setBilling_state(bill.getState());
        sa.setBilling_country(bill.getCountry());
        sa.setSite(webUrl);
        sa.setOrder_id(order.getOrderId() + "_" + ConversionUtils.generateShortCode(order.getOrderId() + "", 14));
        sa.setOrder_created_at(DateUtils.getTime());
        sa.setTag(0);
        sa.setUser_agent(UserAgentUtils.getUserAgent());

        ConePushBo.Meta meta = new ConePushBo.Meta();
        meta.setSite_title(webName);

        //产品信息
        List<ConePushBo.Meta.OrderProduct> prods = new ArrayList<>();
        List<OrderProduct> products = JSONArray.parseArray(order.getProduct(), OrderProduct.class);
        for (OrderProduct product : products) {
            ConePushBo.Meta.OrderProduct obj = new ConePushBo.Meta.OrderProduct();
            obj.setName(product.getName());
            obj.setImage(product.getImage());
            obj.setAttr(new ArrayList<>());
            obj.setQty(product.getQuantity());
            obj.setItemprice(product.getPrice());
            obj.setId(product.getPostId());
            obj.setSku("");
            obj.setUrl("");
            obj.setSubtotal(product.getSubtotal());
            prods.add(obj);
        }
        meta.setOrder_products(prods);

        // 订单金额
        List<Meta.OrderTotal> totals = new ArrayList<>();
        totals.add(new Meta.OrderTotal("Subtotal:", currency + amount));
        totals.add(new Meta.OrderTotal("Shipping:", currency + "0"));
        totals.add(new Meta.OrderTotal("Total:", currency + amount));
        meta.setOrder_totals(totals);

        //写入元数据参数
        sa.setMeta(meta);

        sa.setFang_orderid(sa.getOrder_id());
        sa.setFang_ordersn(sa.getOrder_id());
        sa.setNotifyUrl(gatewayDomain + "/api/system/external/updateCq");
        sa.setId(spayId);
        sa.setMoney_pro(amount + "");
        sa.setMoney_total(amount + "");
        sa.setMoney_shipping("0.00");
        sa.setMoney_payfee("0.00");
        sa.setMoney_other("0.00");
        sa.setMoney_discount("0.00");
        sa.setContent("");
        sa.setMoney_type(currency);
        sa.setCustomer_info("");
        sa.setRequest_url(gatewayDomain);
        sa.setTime(System.currentTimeMillis() / 1000);

        return JSONObject.from(sa, JSONWriter.Feature.WriteMapNullValue);
    }
}
